{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Mix-in\n",
    "\n",
    "* Mix-in\n",
    "    * Rubyで多重継承を実現する機能の事。Rubyの代表的な機能\n",
    "* Rubyの継承は単一継承のみ許可\n",
    "    * <font color=\"red\">同時に複数のスーパークラスを持つ「多重継承」</font>は許されていない\n",
    "    * class式で指定できるスーパークラスは必ず一つだけ\n",
    "* あるクラスが持つ機能を他のクラスに適用する場合\n",
    "    * Rubyは、<font color=\"red\">クラスに機能を混ぜ合わせる事で複数のクラスで機能を共有する機能(Mix-in)</font>で実現する\n",
    "    * <font color=\"red\">複数のクラスで適用したい機能は、モジュールというオブジェクトで定義する</font>\n",
    "        * これをクラスに取り込むことで機能を拡張する\n",
    "* モジュールとクラスの相違点\n",
    "    * モジュールは単独ではインスタンス化できない(newできない)\n",
    "    * モジュールは継承できない\n",
    "    * モジュールは他のクラスやモジュールに取り込むことができる\n",
    "* モジュールの目的\n",
    "    * 名前空間の提供\n",
    "    * Mix-inにおける機能の定義"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Bar]\n",
      "[:methodA]\n"
     ]
    },
    {
     "ename": "NoMethodError",
     "evalue": "undefined method `new' for Bar:Module",
     "output_type": "error",
     "traceback": [
      "\u001b[31mNoMethodError\u001b[0m: undefined method `new' for Bar:Module",
      "\u001b[37m(pry):12:in `<main>'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `evaluate_ruby'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:323:in `handle_line'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `catch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `block in eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `catch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:66:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:12:in `eval'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:87:in `execute_request'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:47:in `dispatch'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:37:in `run'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:70:in `run_kernel'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:34:in `run'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/bin/iruby:5:in `<top (required)>'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:22:in `load'\u001b[0m",
      "\u001b[37m/root/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:22:in `<main>'\u001b[0m"
     ]
    }
   ],
   "source": [
    "### モジュールの定義。クラスとの違いはclassの代わりにmoduleを使っているだけ。\n",
    "module Bar\n",
    "  def methodA\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "puts Bar.ancestors\n",
    "puts Bar.instance_methods\n",
    "\n",
    "### newはできない\n",
    "puts Bar.new"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## モジュールのクラスへの取り込み\n",
    "\n",
    "* includeメソッドを使う\n",
    "* モジュールとancestorsの表示について\n",
    "    * ` [FooExt, Bar, Foo, ... `\n",
    "        * 表示は上のようになっているが、BarモジュールのスーパークラスにFooがあるわけでは無い\n",
    "            * そもそもモジュールにスーパークラスは無い\n",
    "        * FooExt : クラス\n",
    "        * Bar : FooExtがincludeしているモジュール\n",
    "        * Foo : FooExtのスーパークラス\n",
    "    * includeが実行されるとインタプリタは指定したモジュールに対応する無名クラスを作成して、スーパークラスとの間に組み込む\n",
    "    * 無名クラス\n",
    "        * モジュールをincludeしたクラスはモジュールのメソッドを使える\n",
    "        * モジュール名のクラス(Barクラス)ができているわけでは無く、無名のクラスのメソッドとして使っている\n",
    "    * 無名クラスはRubyインタプリタの実装の都合上作られる\n",
    "        * <font color=\"red\">配列の順が継承チェーンの順を必ずしも表しているわけではない</font>\n",
    "        * でも大体は右に行くほど汎用的なクラス\n",
    "        * 同名のメソッドがあった場合に優先的に使われるクラス・モジュールの順？        \n",
    "    * ユーザーに意識させたくないので、superclassで参照できないようにしている"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### FooExtのスーパークラスはFoo(Barモジュールではない)\n",
      "Foo\n",
      "### FooExtの親クラス(includeしているモジュール含む)を配列で表示\n",
      "[FooExt, Bar, Foo, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "### includeではなくprepend(特異メソッドの項目で解説)を使うとモジュールが優先的に使われるためFooExt2よりもBarが先に表示される\n",
      "[Bar, FooExt2, Foo, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "\n",
      "### BarモジュールにmethodAがある\n",
      "[:methodA]\n",
      "### FooExtクラスはincludeしているBarモジュールのmethodAが利用可能\n",
      "[:methodB, :methodA, :pry, :__binding__, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, :to_json, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :public_method, :singleton_method, :is_a?, :extend, :define_singleton_method, :method, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :gem, :to_s, :pretty_inspect, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]\n"
     ]
    }
   ],
   "source": [
    "module Bar\n",
    "  def methodA\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "class Foo; end\n",
    "\n",
    "### Fooクラスを継承したFooExtクラスにBarモジュールを取り込む\n",
    "class FooExt < Foo\n",
    "  include Bar\n",
    "end\n",
    "\n",
    "class FooExt2 < Foo\n",
    "  prepend Bar\n",
    "end\n",
    "\n",
    "puts \"### FooExtのスーパークラスはFoo(Barモジュールではない)\"\n",
    "puts FooExt.superclass\n",
    "puts \"### FooExtの親クラス(includeしているモジュール含む)を配列で表示\"\n",
    "puts FooExt.ancestors\n",
    "puts \"### includeではなくprepend(特異メソッドの項目で解説)を使うとモジュールが優先的に使われるためFooExt2よりもBarが先に表示される\"\n",
    "puts FooExt2.ancestors\n",
    "puts \"\"\n",
    "\n",
    "puts \"### BarモジュールにmethodAがある\"\n",
    "puts Bar.instance_methods(false)\n",
    "puts \"### FooExtクラスはincludeしているBarモジュールのmethodAが利用可能\"\n",
    "puts FooExt.instance_methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 複数モジュールをincludeした場合のメソッドの優先順位\n",
    "\n",
    "* 後にincludeした方が優先される"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "後でincludeしたモジュールBのmethodCが実行される\n",
      "moduleB methodC\n",
      "[Hoge, B, A, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n"
     ]
    }
   ],
   "source": [
    "module A\n",
    "  def methodC\n",
    "    print \"moduleA methodC\"\n",
    "  end\n",
    "end\n",
    "\n",
    "module B\n",
    "  def methodC\n",
    "    print \"moduleB methodC\"\n",
    "  end\n",
    "end\n",
    "\n",
    "class Hoge\n",
    "  include A\n",
    "  include B\n",
    "end\n",
    "\n",
    "hoge = Hoge.new\n",
    "puts \"後でincludeしたモジュールBのmethodCが実行される\"\n",
    "puts hoge.methodC\n",
    "puts Hoge.ancestors"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
